home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2005 October / PCWOCT05.iso / Software / FromTheMag / XAMPP 1.4.14 / xampp-win32-1.4.14-installer.exe / xampp / php / pear / go-pear-bundle / Tar.php < prev   
PHP Script  |  2004-03-25  |  57KB  |  1,599 lines

  1. <?php
  2. /* vim: set ts=4 sw=4: */
  3. // +----------------------------------------------------------------------+
  4. // | PHP Version 4                                                        |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1997-2003 The PHP Group                                |
  7. // +----------------------------------------------------------------------+
  8. // | This source file is subject to version 2.02 of the PHP license,      |
  9. // | that is bundled with this package in the file LICENSE, and is        |
  10. // | available at through the world-wide-web at                           |
  11. // | http://www.php.net/license/2_02.txt.                                 |
  12. // | If you did not receive a copy of the PHP license and are unable to   |
  13. // | obtain it through the world-wide-web, please send a note to          |
  14. // | license@php.net so we can mail you a copy immediately.               |
  15. // +----------------------------------------------------------------------+
  16. // | Author: Vincent Blavet <vincent@blavet.net>                          |
  17. // +----------------------------------------------------------------------+
  18. //
  19. // $Id: Tar.php,v 1.13 2003/05/28 07:08:43 vblavet Exp $
  20.  
  21. require_once 'PEAR.php';
  22.  
  23. /**
  24. * Creates a (compressed) Tar archive
  25. *
  26. * @author   Vincent Blavet <vincent@blavet.net>
  27. * @version  $Revision: 1.13 $
  28. * @package  Archive
  29. */
  30. class Archive_Tar extends PEAR
  31. {
  32.     /**
  33.     * @var string Name of the Tar
  34.     */
  35.     var $_tarname='';
  36.  
  37.     /**
  38.     * @var boolean if true, the Tar file will be gzipped
  39.     */
  40.     var $_compress=false;
  41.  
  42.     /**
  43.     * @var string Type of compression : 'none', 'gz' or 'bz2'
  44.     */
  45.     var $_compress_type='none';
  46.  
  47.     /**
  48.     * @var file descriptor
  49.     */
  50.     var $_file=0;
  51.  
  52.     /**
  53.     * @var string Local Tar name of a remote Tar (http:// or ftp://)
  54.     */
  55.     var $_temp_tarname='';
  56.  
  57.     // {{{ constructor
  58.     /**
  59.     * Archive_Tar Class constructor. This flavour of the constructor only
  60.     * declare a new Archive_Tar object, identifying it by the name of the
  61.     * tar file.
  62.     * If the compress argument is set the tar will be read or created as a
  63.     * gzip or bz2 compressed TAR file.
  64.     *
  65.     * @param    string  $p_tarname  The name of the tar archive to create
  66.     * @param    string  $p_compress can be null, 'gz' or 'bz2'. This
  67.     *                   parameter indicates if gzip or bz2 compression
  68.     *                   is required.  For compatibility reason the
  69.     *                   boolean value 'true' means 'gz'.
  70.     * @access public
  71.     */
  72.     function Archive_Tar($p_tarname, $p_compress = null)
  73.     {
  74.         $this->PEAR();
  75.         $this->_compress = false;
  76.         $this->_compress_type = 'none';
  77.         if ($p_compress === null) {
  78.             if (@file_exists($p_tarname)) {
  79.                 if ($fp = @fopen($p_tarname, "rb")) {
  80.                     // look for gzip magic cookie
  81.                     $data = fread($fp, 2);
  82.                     fclose($fp);
  83.                     if ($data == "\37\213") {
  84.                         $this->_compress = true;
  85.                         $this->_compress_type = 'gz';
  86.                     // No sure it's enought for a magic code ....
  87.                     } elseif ($data == "BZ") {
  88.                         $this->_compress = true;
  89.                         $this->_compress_type = 'bz2';
  90.                     }
  91.                 }
  92.             } else {
  93.                 // probably a remote file or some file accessible
  94.                 // through a stream interface
  95.                 if (substr($p_tarname, -2) == 'gz') {
  96.                     $this->_compress = true;
  97.                     $this->_compress_type = 'gz';
  98.                 } elseif ((substr($p_tarname, -3) == 'bz2') ||
  99.                           (substr($p_tarname, -2) == 'bz')) {
  100.                     $this->_compress = true;
  101.                     $this->_compress_type = 'bz2';
  102.                 }
  103.             }
  104.         } else {
  105.             if (($p_compress == true) || ($p_compress == 'gz')) {
  106.                 $this->_compress = true;
  107.                 $this->_compress_type = 'gz';
  108.             } else if ($p_compress == 'bz2') {
  109.                 $this->_compress = true;
  110.                 $this->_compress_type = 'bz2';
  111.             }
  112.         }
  113.         $this->_tarname = $p_tarname;
  114.         if ($this->_compress) { // assert zlib or bz2 extension support
  115.             if ($this->_compress_type == 'gz')
  116.                 $extname = 'zlib';
  117.             else if ($this->_compress_type == 'bz2')
  118.                 $extname = 'bz2';
  119.  
  120.             if (!extension_loaded($extname)) {
  121.                 PEAR::loadExtension($extname);
  122.             }
  123.             if (!extension_loaded($extname)) {
  124.                 die("The extension '$extname' couldn't be found.\n".
  125.                     "Please make sure your version of PHP was built ".
  126.                     "with '$extname' support.\n");
  127.                 return false;
  128.             }
  129.         }
  130.     }
  131.     // }}}
  132.  
  133.     // {{{ destructor
  134.     function _Archive_Tar()
  135.     {
  136.         $this->_close();
  137.         // ----- Look for a local copy to delete
  138.         if ($this->_temp_tarname != '')
  139.             @unlink($this->_temp_tarname);
  140.         $this->_PEAR();
  141.     }
  142.     // }}}
  143.  
  144.     // {{{ create()
  145.     /**
  146.     * This method creates the archive file and add the files / directories
  147.     * that are listed in $p_filelist.
  148.     * If a file with the same name exist and is writable, it is replaced
  149.     * by the new tar.
  150.     * The method return false and a PEAR error text.
  151.     * The $p_filelist parameter can be an array of string, each string
  152.     * representing a filename or a directory name with their path if
  153.     * needed. It can also be a single string with names separated by a
  154.     * single blank.
  155.     * For each directory added in the archive, the files and
  156.     * sub-directories are also added.
  157.     * See also createModify() method for more details.
  158.     *
  159.     * @param array  $p_filelist An array of filenames and directory names, or a single
  160.     *                           string with names separated by a single blank space.
  161.     * @return                   true on success, false on error.
  162.     * @see createModify()
  163.     * @access public
  164.     */
  165.     function create($p_filelist)
  166.     {
  167.         return $this->createModify($p_filelist, '', '');
  168.     }
  169.     // }}}
  170.  
  171.     // {{{ add()
  172.     /**
  173.     * This method add the files / directories that are listed in $p_filelist in
  174.     * the archive. If the archive does not exist it is created.
  175.     * The method return false and a PEAR error text.
  176.     * The files and directories listed are only added at the end of the archive,
  177.     * even if a file with the same name is already archived.
  178.     * See also createModify() method for more details.
  179.     *
  180.     * @param array  $p_filelist An array of filenames and directory names, or a single
  181.     *                           string with names separated by a single blank space.
  182.     * @return                   true on success, false on error.
  183.     * @see createModify()
  184.     * @access public
  185.     */
  186.     function add($p_filelist)
  187.     {
  188.         return $this->addModify($p_filelist, '', '');
  189.     }
  190.     // }}}
  191.  
  192.     // {{{ extract()
  193.     function extract($p_path='')
  194.     {
  195.         return $this->extractModify($p_path, '');
  196.     }
  197.     // }}}
  198.  
  199.     // {{{ listContent()
  200.     function listContent()
  201.     {
  202.         $v_list_detail = array();
  203.  
  204.         if ($this->_openRead()) {
  205.             if (!$this->_extractList('', $v_list_detail, "list", '', '')) {
  206.                 unset($v_list_detail);
  207.                 $v_list_detail = 0;
  208.             }
  209.             $this->_close();
  210.         }
  211.  
  212.         return $v_list_detail;
  213.     }
  214.     // }}}
  215.  
  216.     // {{{ createModify()
  217.     /**
  218.     * This method creates the archive file and add the files / directories
  219.     * that are listed in $p_filelist.
  220.     * If the file already exists and is writable, it is replaced by the
  221.     * new tar. It is a create and not an add. If the file exists and is
  222.     * read-only or is a directory it is not replaced. The method return
  223.     * false and a PEAR error text.
  224.     * The $p_filelist parameter can be an array of string, each string
  225.     * representing a filename or a directory name with their path if
  226.     * needed. It can also be a single string with names separated by a
  227.     * single blank.
  228.     * The path indicated in $p_remove_dir will be removed from the
  229.     * memorized path of each file / directory listed when this path
  230.     * exists. By default nothing is removed (empty path '')
  231.     * The path indicated in $p_add_dir will be added at the beginning of
  232.     * the memorized path of each file / directory listed. However it can
  233.     * be set to empty ''. The adding of a path is done after the removing
  234.     * of path.
  235.     * The path add/remove ability enables the user to prepare an archive
  236.     * for extraction in a different path than the origin files are.
  237.     * See also addModify() method for file adding properties.
  238.     *
  239.     * @param array  $p_filelist     An array of filenames and directory names, or a single
  240.     *                               string with names separated by a single blank space.
  241.     * @param string $p_add_dir      A string which contains a path to be added to the
  242.     *                               memorized path of each element in the list.
  243.     * @param string $p_remove_dir   A string which contains a path to be removed from
  244.     *                               the memorized path of each element in the list, when
  245.     *                               relevant.
  246.     * @return boolean               true on success, false on error.
  247.     * @access public
  248.     * @see addModify()
  249.     */
  250.     function createModify($p_filelist, $p_add_dir, $p_remove_dir='')
  251.     {
  252.         $v_result = true;
  253.  
  254.         if (!$this->_openWrite())
  255.             return false;
  256.  
  257.         if ($p_filelist != '') {
  258.             if (is_array($p_filelist))
  259.                 $v_list = $p_filelist;
  260.             elseif (is_string($p_filelist))
  261.                 $v_list = explode(" ", $p_filelist);
  262.             else {
  263.                 $this->_cleanFile();
  264.                 $this->_error('Invalid file list');
  265.                 return false;
  266.             }
  267.  
  268.             $v_result = $this->_addList($v_list, $p_add_dir, $p_remove_dir);
  269.         }
  270.  
  271.         if ($v_result) {
  272.             $this->_writeFooter();
  273.             $this->_close();
  274.         } else
  275.             $this->_cleanFile();
  276.  
  277.         return $v_result;
  278.     }
  279.     // }}}
  280.  
  281.     // {{{ addModify()
  282.     /**
  283.     * This method add the files / directories listed in $p_filelist at the
  284.     * end of the existing archive. If the archive does not yet exists it
  285.     * is created.
  286.     * The $p_filelist parameter can be an array of string, each string
  287.     * representing a filename or a directory name with their path if
  288.     * needed. It can also be a single string with names separated by a
  289.     * single blank.
  290.     * The path indicated in $p_remove_dir will be removed from the
  291.     * memorized path of each file / directory listed when this path
  292.     * exists. By default nothing is removed (empty path '')
  293.     * The path indicated in $p_add_dir will be added at the beginning of
  294.     * the memorized path of each file / directory listed. However it can
  295.     * be set to empty ''. The adding of a path is done after the removing
  296.     * of path.
  297.     * The path add/remove ability enables the user to prepare an archive
  298.     * for extraction in a different path than the origin files are.
  299.     * If a file/dir is already in the archive it will only be added at the
  300.     * end of the archive. There is no update of the existing archived
  301.     * file/dir. However while extracting the archive, the last file will
  302.     * replace the first one. This results in a none optimization of the
  303.     * archive size.
  304.     * If a file/dir does not exist the file/dir is ignored. However an
  305.     * error text is send to PEAR error.
  306.     * If a file/dir is not readable the file/dir is ignored. However an
  307.     * error text is send to PEAR error.
  308.     *
  309.     * @param array      $p_filelist     An array of filenames and directory names, or a single
  310.     *                                   string with names separated by a single blank space.
  311.     * @param string     $p_add_dir      A string which contains a path to be added to the
  312.     *                                   memorized path of each element in the list.
  313.     * @param string     $p_remove_dir   A string which contains a path to be removed from
  314.     *                                   the memorized path of each element in the list, when
  315.     *                                   relevant.
  316.     * @return                           true on success, false on error.
  317.     * @access public
  318.     */
  319.     function addModify($p_filelist, $p_add_dir, $p_remove_dir='')
  320.     {
  321.         $v_result = true;
  322.  
  323.         if (!@is_file($this->_tarname))
  324.             $v_result = $this->createModify($p_filelist, $p_add_dir, $p_remove_dir);
  325.         else {
  326.             if (is_array($p_filelist))
  327.                 $v_list = $p_filelist;
  328.             elseif (is_string($p_filelist))
  329.                 $v_list = explode(" ", $p_filelist);
  330.             else {
  331.                 $this->_error('Invalid file list');
  332.                 return false;
  333.             }
  334.  
  335.             $v_result = $this->_append($v_list, $p_add_dir, $p_remove_dir);
  336.         }
  337.  
  338.         return $v_result;
  339.     }
  340.     // }}}
  341.  
  342.     // {{{ addString()
  343.     /**
  344.     * This method add a single string as a file at the
  345.     * end of the existing archive. If the archive does not yet exists it
  346.     * is created.
  347.     *
  348.     * @param string     $p_filename     A string which contains the full filename path
  349.     *                                   that will be associated with the string.
  350.     * @param string     $p_string       The content of the file added in the archive.
  351.     * @return                           true on success, false on error.
  352.     * @access public
  353.     */
  354.     function addString($p_filename, $p_string)
  355.     {
  356.         $v_result = true;
  357.  
  358.         if (!@is_file($this->_tarname)) {
  359.             if (!$this->_openWrite()) {
  360.                 return false;
  361.             }
  362.             $this->_close();
  363.         }
  364.         
  365.         if (!$this->_openAppend())
  366.             return false;
  367.  
  368.         // Need to check the get back to the temporary file ? ....
  369.         $v_result = $this->_addString($p_filename, $p_string);
  370.  
  371.         $this->_writeFooter();
  372.  
  373.         $this->_close();
  374.  
  375.         return $v_result;
  376.     }
  377.     // }}}
  378.  
  379.     // {{{ extractModify()
  380.     /**
  381.     * This method extract all the content of the archive in the directory
  382.     * indicated by $p_path. When relevant the memorized path of the
  383.     * files/dir can be modified by removing the $p_remove_path path at the
  384.     * beginning of the file/dir path.
  385.     * While extracting a file, if the directory path does not exists it is
  386.     * created.
  387.     * While extracting a file, if the file already exists it is replaced
  388.     * without looking for last modification date.
  389.     * While extracting a file, if the file already exists and is write
  390.     * protected, the extraction is aborted.
  391.     * While extracting a file, if a directory with the same name already
  392.     * exists, the extraction is aborted.
  393.     * While extracting a directory, if a file with the same name already
  394.     * exists, the extraction is aborted.
  395.     * While extracting a file/directory if the destination directory exist
  396.     * and is write protected, or does not exist but can not be created,
  397.     * the extraction is aborted.
  398.     * If after extraction an extracted file does not show the correct
  399.     * stored file size, the extraction is aborted.
  400.     * When the extraction is aborted, a PEAR error text is set and false
  401.     * is returned. However the result can be a partial extraction that may
  402.     * need to be manually cleaned.
  403.     *
  404.     * @param string $p_path         The path of the directory where the files/dir need to by
  405.     *                               extracted.
  406.     * @param string $p_remove_path  Part of the memorized path that can be removed if
  407.     *                               present at the beginning of the file/dir path.
  408.     * @return boolean               true on success, false on error.
  409.     * @access public
  410.     * @see extractList()
  411.     */
  412.     function extractModify($p_path, $p_remove_path)
  413.     {
  414.         $v_result = true;
  415.         $v_list_detail = array();
  416.  
  417.         if ($v_result = $this->_openRead()) {
  418.             $v_result = $this->_extractList($p_path, $v_list_detail, "complete", 0, $p_remove_path);
  419.             $this->_close();
  420.         }
  421.  
  422.         return $v_result;
  423.     }
  424.     // }}}
  425.  
  426.     // {{{ extractInString()
  427.     /**
  428.     * This method extract from the archive one file identified by $p_filename.
  429.     * The return value is a string with the file content, or NULL on error.
  430.     * @param string $p_filename     The path of the file to extract in a string.
  431.     * @return                       a string with the file content or NULL.
  432.     * @access public
  433.     */
  434.     function extractInString($p_filename)
  435.     {
  436.         if ($this->_openRead()) {
  437.             $v_result = $this->_extractInString($p_filename);
  438.             $this->_close();
  439.         } else {
  440.             $v_result = NULL;
  441.         }
  442.  
  443.         return $v_result;
  444.     }
  445.     // }}}
  446.  
  447.     // {{{ extractList()
  448.     /**
  449.     * This method extract from the archive only the files indicated in the
  450.     * $p_filelist. These files are extracted in the current directory or
  451.     * in the directory indicated by the optional $p_path parameter.
  452.     * If indicated the $p_remove_path can be used in the same way as it is
  453.     * used in extractModify() method.
  454.     * @param array  $p_filelist     An array of filenames and directory names, or a single
  455.     *                               string with names separated by a single blank space.
  456.     * @param string $p_path         The path of the directory where the files/dir need to by
  457.     *                               extracted.
  458.     * @param string $p_remove_path  Part of the memorized path that can be removed if
  459.     *                               present at the beginning of the file/dir path.
  460.     * @return                       true on success, false on error.
  461.     * @access public
  462.     * @see extractModify()
  463.     */
  464.     function extractList($p_filelist, $p_path='', $p_remove_path='')
  465.     {
  466.         $v_result = true;
  467.         $v_list_detail = array();
  468.  
  469.         if (is_array($p_filelist))
  470.             $v_list = $p_filelist;
  471.         elseif (is_string($p_filelist))
  472.             $v_list = explode(" ", $p_filelist);
  473.         else {
  474.             $this->_error('Invalid string list');
  475.             return false;
  476.         }
  477.  
  478.         if ($v_result = $this->_openRead()) {
  479.             $v_result = $this->_extractList($p_path, $v_list_detail, "partial", $v_list, $p_remove_path);
  480.             $this->_close();
  481.         }
  482.  
  483.         return $v_result;
  484.     }
  485.     // }}}
  486.  
  487.     // {{{ _error()
  488.     function _error($p_message)
  489.     {
  490.         // ----- To be completed
  491.         $this->raiseError($p_message);
  492.     }
  493.     // }}}
  494.  
  495.     // {{{ _warning()
  496.     function _warning($p_message)
  497.     {
  498.         // ----- To be completed
  499.         $this->raiseError($p_message);
  500.     }
  501.     // }}}
  502.  
  503.     // {{{ _openWrite()
  504.     function _openWrite()
  505.     {
  506.         if ($this->_compress_type == 'gz')
  507.             $this->_file = @gzopen($this->_tarname, "wb");
  508.         else if ($this->_compress_type == 'bz2')
  509.             $this->_file = @bzopen($this->_tarname, "wb");
  510.         else if ($this->_compress_type == 'none')
  511.             $this->_file = @fopen($this->_tarname, "wb");
  512.         else
  513.             $this->_error('Unknown or missing compression type ('.$this->_compress_type.')');
  514.  
  515.         if ($this->_file == 0) {
  516.             $this->_error('Unable to open in write mode \''.$this->_tarname.'\'');
  517.             return false;
  518.         }
  519.  
  520.         return true;
  521.     }
  522.     // }}}
  523.  
  524.     // {{{ _openRead()
  525.     function _openRead()
  526.     {
  527.         if (strtolower(substr($this->_tarname, 0, 7)) == 'http://') {
  528.  
  529.           // ----- Look if a local copy need to be done
  530.           if ($this->_temp_tarname == '') {
  531.               $this->_temp_tarname = uniqid('tar').'.tmp';
  532.               if (!$v_file_from = @fopen($this->_tarname, 'rb')) {
  533.                 $this->_error('Unable to open in read mode \''.$this->_tarname.'\'');
  534.                 $this->_temp_tarname = '';
  535.                 return false;
  536.               }
  537.               if (!$v_file_to = @fopen($this->_temp_tarname, 'wb')) {
  538.                 $this->_error('Unable to open in write mode \''.$this->_temp_tarname.'\'');
  539.                 $this->_temp_tarname = '';
  540.                 return false;
  541.               }
  542.               while ($v_data = @fread($v_file_from, 1024))
  543.                   @fwrite($v_file_to, $v_data);
  544.               @fclose($v_file_from);
  545.               @fclose($v_file_to);
  546.           }
  547.  
  548.           // ----- File to open if the local copy
  549.           $v_filename = $this->_temp_tarname;
  550.  
  551.         } else
  552.           // ----- File to open if the normal Tar file
  553.           $v_filename = $this->_tarname;
  554.  
  555.         if ($this->_compress_type == 'gz')
  556.             $this->_file = @gzopen($v_filename, "rb");
  557.         else if ($this->_compress_type == 'bz2')
  558.             $this->_file = @bzopen($v_filename, "rb");
  559.         else if ($this->_compress_type == 'none')
  560.             $this->_file = @fopen($v_filename, "rb");
  561.         else
  562.             $this->_error('Unknown or missing compression type ('.$this->_compress_type.')');
  563.  
  564.         if ($this->_file == 0) {
  565.             $this->_error('Unable to open in read mode \''.$v_filename.'\'');
  566.             return false;
  567.         }
  568.  
  569.         return true;
  570.     }
  571.     // }}}
  572.  
  573.     // {{{ _openReadWrite()
  574.     function _openReadWrite()
  575.     {
  576.         if ($this->_compress_type == 'gz')
  577.             $this->_file = @gzopen($this->_tarname, "r+b");
  578.         else if ($this->_compress_type == 'bz2')
  579.             $this->_file = @bzopen($this->_tarname, "r+b");
  580.         else if ($this->_compress_type == 'none')
  581.             $this->_file = @fopen($this->_tarname, "r+b");
  582.         else
  583.             $this->_error('Unknown or missing compression type ('.$this->_compress_type.')');
  584.  
  585.         if ($this->_file == 0) {
  586.             $this->_error('Unable to open in read/write mode \''.$this->_tarname.'\'');
  587.             return false;
  588.         }
  589.  
  590.         return true;
  591.     }
  592.     // }}}
  593.  
  594.     // {{{ _close()
  595.     function _close()
  596.     {
  597.         if (isset($this->_file)) {
  598.             if ($this->_compress_type == 'gz')
  599.                 @gzclose($this->_file);
  600.             else if ($this->_compress_type == 'bz2')
  601.                 @bzclose($this->_file);
  602.             else if ($this->_compress_type == 'none')
  603.                 @fclose($this->_file);
  604.             else
  605.                 $this->_error('Unknown or missing compression type ('.$this->_compress_type.')');
  606.  
  607.             $this->_file = 0;
  608.         }
  609.  
  610.         // ----- Look if a local copy need to be erase
  611.         // Note that it might be interesting to keep the url for a time : ToDo
  612.         if ($this->_temp_tarname != '') {
  613.             @unlink($this->_temp_tarname);
  614.             $this->_temp_tarname = '';
  615.         }
  616.  
  617.         return true;
  618.     }
  619.     // }}}
  620.  
  621.     // {{{ _cleanFile()
  622.     function _cleanFile()
  623.     {
  624.         $this->_close();
  625.  
  626.         // ----- Look for a local copy
  627.         if ($this->_temp_tarname != '') {
  628.             // ----- Remove the local copy but not the remote tarname
  629.             @unlink($this->_temp_tarname);
  630.             $this->_temp_tarname = '';
  631.         } else {
  632.             // ----- Remove the local tarname file
  633.             @unlink($this->_tarname);
  634.         }
  635.         $this->_tarname = '';
  636.  
  637.         return true;
  638.     }
  639.     // }}}
  640.  
  641.     // {{{ _writeBlock()
  642.     function _writeBlock($p_binary_data, $p_len=null)
  643.     {
  644.       if ($this->_file) {
  645.           if ($p_len === null) {
  646.               if ($this->_compress_type == 'gz')
  647.                   @gzputs($this->_file, $p_binary_data);
  648.               else if ($this->_compress_type == 'bz2')
  649.                   @bzwrite($this->_file, $p_binary_data);
  650.               else if ($this->_compress_type == 'none')
  651.                   @fputs($this->_file, $p_binary_data);
  652.               else
  653.                   $this->_error('Unknown or missing compression type ('.$this->_compress_type.')');
  654.           } else {
  655.               if ($this->_compress_type == 'gz')
  656.                   @gzputs($this->_file, $p_binary_data, $p_len);
  657.               else if ($this->_compress_type == 'bz2')
  658.                   @bzwrite($this->_file, $p_binary_data, $p_len);
  659.               else if ($this->_compress_type == 'none')
  660.                   @fputs($this->_file, $p_binary_data, $p_len);
  661.               else
  662.                   $this->_error('Unknown or missing compression type ('.$this->_compress_type.')');
  663.  
  664.           }
  665.       }
  666.       return true;
  667.     }
  668.     // }}}
  669.  
  670.     // {{{ _readBlock()
  671.     function _readBlock($p_len=null)
  672.     {
  673.       $v_block = null;
  674.       if ($this->_file) {
  675.           if ($p_len === null)
  676.               $p_len = 512;
  677.               
  678.           if ($this->_compress_type == 'gz')
  679.               $v_block = @gzread($this->_file, 512);
  680.           else if ($this->_compress_type == 'bz2')
  681.               $v_block = @bzread($this->_file, 512);
  682.           else if ($this->_compress_type == 'none')
  683.               $v_block = @fread($this->_file, 512);
  684.           else
  685.               $this->_error('Unknown or missing compression type ('.$this->_compress_type.')');
  686.  
  687.       }
  688.       return $v_block;
  689.     }
  690.     // }}}
  691.  
  692.     // {{{ _jumpBlock()
  693.     function _jumpBlock($p_len=null)
  694.     {
  695.       if ($this->_file) {
  696.           if ($p_len === null)
  697.               $p_len = 1;
  698.  
  699.           if ($this->_compress_type == 'gz')
  700.               @gzseek($this->_file, @gztell($this->_file)+($p_len*512));
  701.           else if ($this->_compress_type == 'bz2') {
  702.               // ----- Replace missing bztell() and bzseek()
  703.               for ($i=0; $i<$p_len; $i++)
  704.                   $this->_readBlock();
  705.           } else if ($this->_compress_type == 'none')
  706.               @fseek($this->_file, @ftell($this->_file)+($p_len*512));
  707.           else
  708.               $this->_error('Unknown or missing compression type ('.$this->_compress_type.')');
  709.  
  710.       }
  711.       return true;
  712.     }
  713.     // }}}
  714.  
  715.     // {{{ _writeFooter()
  716.     function _writeFooter()
  717.     {
  718.       if ($this->_file) {
  719.           // ----- Write the last 0 filled block for end of archive
  720.           $v_binary_data = pack("a512", '');
  721.           $this->_writeBlock($v_binary_data);
  722.       }
  723.       return true;
  724.     }
  725.     // }}}
  726.  
  727.     // {{{ _addList()
  728.     function _addList($p_list, $p_add_dir, $p_remove_dir)
  729.     {
  730.       $v_result=true;
  731.       $v_header = array();
  732.  
  733.       // ----- Remove potential windows directory separator
  734.       $p_add_dir = $this->_translateWinPath($p_add_dir);
  735.       $p_remove_dir = $this->_translateWinPath($p_remove_dir, false);
  736.  
  737.       if (!$this->_file) {
  738.           $this->_error('Invalid file descriptor');
  739.           return false;
  740.       }
  741.  
  742.       if (sizeof($p_list) == 0)
  743.           return true;
  744.  
  745.       for ($j=0; ($j<count($p_list)) && ($v_result); $j++) {
  746.         $v_filename = $p_list[$j];
  747.  
  748.         // ----- Skip the current tar name
  749.         if ($v_filename == $this->_tarname)
  750.             continue;
  751.  
  752.         if ($v_filename == '')
  753.             continue;
  754.  
  755.         if (!file_exists($v_filename)) {
  756.             $this->_warning("File '$v_filename' does not exist");
  757.             continue;
  758.         }
  759.  
  760.         // ----- Add the file or directory header
  761.         if (!$this->_addFile($v_filename, $v_header, $p_add_dir, $p_remove_dir))
  762.             return false;
  763.  
  764.         if (@is_dir($v_filename)) {
  765.             if (!($p_hdir = opendir($v_filename))) {
  766.                 $this->_warning("Directory '$v_filename' can not be read");
  767.                 continue;
  768.             }
  769.             $p_hitem = readdir($p_hdir); // '.' directory
  770.             $p_hitem = readdir($p_hdir); // '..' directory
  771.             while (false !== ($p_hitem = readdir($p_hdir))) {
  772.                 if ($v_filename != ".")
  773.                     $p_temp_list[0] = $v_filename.'/'.$p_hitem;
  774.                 else
  775.                     $p_temp_list[0] = $p_hitem;
  776.  
  777.                 $v_result = $this->_addList($p_temp_list, $p_add_dir, $p_remove_dir);
  778.             }
  779.  
  780.             unset($p_temp_list);
  781.             unset($p_hdir);
  782.             unset($p_hitem);
  783.         }
  784.       }
  785.  
  786.       return $v_result;
  787.     }
  788.     // }}}
  789.  
  790.     // {{{ _addFile()
  791.     function _addFile($p_filename, &$p_header, $p_add_dir, $p_remove_dir)
  792.     {
  793.       if (!$this->_file) {
  794.           $this->_error('Invalid file descriptor');
  795.           return false;
  796.       }
  797.  
  798.       if ($p_filename == '') {
  799.           $this->_error('Invalid file name');
  800.           return false;
  801.       }
  802.  
  803.       // ----- Calculate the stored filename
  804.       $p_filename = $this->_translateWinPath($p_filename, false);;
  805.       $v_stored_filename = $p_filename;
  806.       if (strcmp($p_filename, $p_remove_dir) == 0) {
  807.           return true;
  808.       }
  809.       if ($p_remove_dir != '') {
  810.           if (substr($p_remove_dir, -1) != '/')
  811.               $p_remove_dir .= '/';
  812.  
  813.           if (substr($p_filename, 0, strlen($p_remove_dir)) == $p_remove_dir)
  814.               $v_stored_filename = substr($p_filename, strlen($p_remove_dir));
  815.       }
  816.       $v_stored_filename = $this->_translateWinPath($v_stored_filename);
  817.       if ($p_add_dir != '') {
  818.           if (substr($p_add_dir, -1) == '/')
  819.               $v_stored_filename = $p_add_dir.$v_stored_filename;
  820.           else
  821.               $v_stored_filename = $p_add_dir.'/'.$v_stored_filename;
  822.       }
  823.  
  824.       $v_stored_filename = $this->_pathReduction($v_stored_filename);
  825.  
  826.       if (is_file($p_filename)) {
  827.           if (($v_file = @fopen($p_filename, "rb")) == 0) {
  828.               $this->_warning("Unable to open file '$p_filename' in binary read mode");
  829.               return true;
  830.           }
  831.  
  832.           if (!$this->_writeHeader($p_filename, $v_stored_filename))
  833.               return false;
  834.  
  835.           while (($v_buffer = fread($v_file, 512)) != '') {
  836.               $v_binary_data = pack("a512", "$v_buffer");
  837.               $this->_writeBlock($v_binary_data);
  838.           }
  839.  
  840.           fclose($v_file);
  841.  
  842.       } else {
  843.           // ----- Only header for dir
  844.           if (!$this->_writeHeader($p_filename, $v_stored_filename))
  845.               return false;
  846.       }
  847.  
  848.       return true;
  849.     }
  850.     // }}}
  851.  
  852.     // {{{ _addString()
  853.     function _addString($p_filename, $p_string)
  854.     {
  855.       if (!$this->_file) {
  856.           $this->_error('Invalid file descriptor');
  857.           return false;
  858.       }
  859.  
  860.       if ($p_filename == '') {
  861.           $this->_error('Invalid file name');
  862.           return false;
  863.       }
  864.  
  865.       // ----- Calculate the stored filename
  866.       $p_filename = $this->_translateWinPath($p_filename, false);;
  867.  
  868.       if (!$this->_writeHeaderBlock($p_filename, strlen($p_string), 0, 0, "", 0, 0))
  869.           return false;
  870.  
  871.       $i=0;
  872.       while (($v_buffer = substr($p_string, (($i++)*512), 512)) != '') {
  873.           $v_binary_data = pack("a512", $v_buffer);
  874.           $this->_writeBlock($v_binary_data);
  875.       }
  876.  
  877.       return true;
  878.     }
  879.     // }}}
  880.  
  881.     // {{{ _writeHeader()
  882.     function _writeHeader($p_filename, $p_stored_filename)
  883.     {
  884.         if ($p_stored_filename == '')
  885.             $p_stored_filename = $p_filename;
  886.         $v_reduce_filename = $this->_pathReduction($p_stored_filename);
  887.  
  888.         if (strlen($v_reduce_filename) > 99) {
  889.           if (!$this->_writeLongHeader($v_reduce_filename))
  890.             return false;
  891.         }
  892.  
  893.         $v_info = stat($p_filename);
  894.         $v_uid = sprintf("%6s ", DecOct($v_info[4]));
  895.         $v_gid = sprintf("%6s ", DecOct($v_info[5]));
  896.         $v_perms = sprintf("%6s ", DecOct(fileperms($p_filename)));
  897.  
  898.         $v_mtime = sprintf("%11s", DecOct(filemtime($p_filename)));
  899.  
  900.         if (@is_dir($p_filename)) {
  901.           $v_typeflag = "5";
  902.           $v_size = sprintf("%11s ", DecOct(0));
  903.         } else {
  904.           $v_typeflag = '';
  905.           clearstatcache();
  906.           $v_size = sprintf("%11s ", DecOct(filesize($p_filename)));
  907.         }
  908.  
  909.         $v_linkname = '';
  910.  
  911.         $v_magic = '';
  912.  
  913.         $v_version = '';
  914.  
  915.         $v_uname = '';
  916.  
  917.         $v_gname = '';
  918.  
  919.         $v_devmajor = '';
  920.  
  921.         $v_devminor = '';
  922.  
  923.         $v_prefix = '';
  924.  
  925.         $v_binary_data_first = pack("a100a8a8a8a12A12", $v_reduce_filename, $v_perms, $v_uid, $v_gid, $v_size, $v_mtime);
  926.         $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12", $v_typeflag, $v_linkname, $v_magic, $v_version, $v_uname, $v_gname, $v_devmajor, $v_devminor, $v_prefix, '');
  927.  
  928.         // ----- Calculate the checksum
  929.         $v_checksum = 0;
  930.         // ..... First part of the header
  931.         for ($i=0; $i<148; $i++)
  932.             $v_checksum += ord(substr($v_binary_data_first,$i,1));
  933.         // ..... Ignore the checksum value and replace it by ' ' (space)
  934.         for ($i=148; $i<156; $i++)
  935.             $v_checksum += ord(' ');
  936.         // ..... Last part of the header
  937.         for ($i=156, $j=0; $i<512; $i++, $j++)
  938.             $v_checksum += ord(substr($v_binary_data_last,$j,1));
  939.  
  940.         // ----- Write the first 148 bytes of the header in the archive
  941.         $this->_writeBlock($v_binary_data_first, 148);
  942.  
  943.         // ----- Write the calculated checksum
  944.         $v_checksum = sprintf("%6s ", DecOct($v_checksum));
  945.         $v_binary_data = pack("a8", $v_checksum);
  946.         $this->_writeBlock($v_binary_data, 8);
  947.  
  948.         // ----- Write the last 356 bytes of the header in the archive
  949.         $this->_writeBlock($v_binary_data_last, 356);
  950.  
  951.         return true;
  952.     }
  953.     // }}}
  954.  
  955.     // {{{ _writeHeaderBlock()
  956.     function _writeHeaderBlock($p_filename, $p_size, $p_mtime=0, $p_perms=0, $p_type='', $p_uid=0, $p_gid=0)
  957.     {
  958.         $p_filename = $this->_pathReduction($p_filename);
  959.  
  960.         if (strlen($p_filename) > 99) {
  961.           if (!$this->_writeLongHeader($p_filename))
  962.             return false;
  963.         }
  964.  
  965.         if ($p_type == "5") {
  966.           $v_size = sprintf("%11s ", DecOct(0));
  967.         } else {
  968.           $v_size = sprintf("%11s ", DecOct($p_size));
  969.         }
  970.  
  971.         $v_uid = sprintf("%6s ", DecOct($p_uid));
  972.         $v_gid = sprintf("%6s ", DecOct($p_gid));
  973.         $v_perms = sprintf("%6s ", DecOct($p_perms));
  974.  
  975.         $v_mtime = sprintf("%11s", DecOct($p_mtime));
  976.  
  977.         $v_linkname = '';
  978.  
  979.         $v_magic = '';
  980.  
  981.         $v_version = '';
  982.  
  983.         $v_uname = '';
  984.  
  985.         $v_gname = '';
  986.  
  987.         $v_devmajor = '';
  988.  
  989.         $v_devminor = '';
  990.  
  991.         $v_prefix = '';
  992.  
  993.         $v_binary_data_first = pack("a100a8a8a8a12A12", $p_filename, $v_perms, $v_uid, $v_gid, $v_size, $v_mtime);
  994.         $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12", $p_type, $v_linkname, $v_magic, $v_version, $v_uname, $v_gname, $v_devmajor, $v_devminor, $v_prefix, '');
  995.  
  996.         // ----- Calculate the checksum
  997.         $v_checksum = 0;
  998.         // ..... First part of the header
  999.         for ($i=0; $i<148; $i++)
  1000.             $v_checksum += ord(substr($v_binary_data_first,$i,1));
  1001.         // ..... Ignore the checksum value and replace it by ' ' (space)
  1002.         for ($i=148; $i<156; $i++)
  1003.             $v_checksum += ord(' ');
  1004.         // ..... Last part of the header
  1005.         for ($i=156, $j=0; $i<512; $i++, $j++)
  1006.             $v_checksum += ord(substr($v_binary_data_last,$j,1));
  1007.  
  1008.         // ----- Write the first 148 bytes of the header in the archive
  1009.         $this->_writeBlock($v_binary_data_first, 148);
  1010.  
  1011.         // ----- Write the calculated checksum
  1012.         $v_checksum = sprintf("%6s ", DecOct($v_checksum));
  1013.         $v_binary_data = pack("a8", $v_checksum);
  1014.         $this->_writeBlock($v_binary_data, 8);
  1015.  
  1016.         // ----- Write the last 356 bytes of the header in the archive
  1017.         $this->_writeBlock($v_binary_data_last, 356);
  1018.  
  1019.         return true;
  1020.     }
  1021.     // }}}
  1022.  
  1023.     // {{{ _writeLongHeader()
  1024.     function _writeLongHeader($p_filename)
  1025.     {
  1026.         $v_size = sprintf("%11s ", DecOct(strlen($p_filename)));
  1027.  
  1028.         $v_typeflag = 'L';
  1029.  
  1030.         $v_linkname = '';
  1031.  
  1032.         $v_magic = '';
  1033.  
  1034.         $v_version = '';
  1035.  
  1036.         $v_uname = '';
  1037.  
  1038.         $v_gname = '';
  1039.  
  1040.         $v_devmajor = '';
  1041.  
  1042.         $v_devminor = '';
  1043.  
  1044.         $v_prefix = '';
  1045.  
  1046.         $v_binary_data_first = pack("a100a8a8a8a12A12", '././@LongLink', 0, 0, 0, $v_size, 0);
  1047.         $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12", $v_typeflag, $v_linkname, $v_magic, $v_version, $v_uname, $v_gname, $v_devmajor, $v_devminor, $v_prefix, '');
  1048.  
  1049.         // ----- Calculate the checksum
  1050.         $v_checksum = 0;
  1051.         // ..... First part of the header
  1052.         for ($i=0; $i<148; $i++)
  1053.             $v_checksum += ord(substr($v_binary_data_first,$i,1));
  1054.         // ..... Ignore the checksum value and replace it by ' ' (space)
  1055.         for ($i=148; $i<156; $i++)
  1056.             $v_checksum += ord(' ');
  1057.         // ..... Last part of the header
  1058.         for ($i=156, $j=0; $i<512; $i++, $j++)
  1059.             $v_checksum += ord(substr($v_binary_data_last,$j,1));
  1060.  
  1061.         // ----- Write the first 148 bytes of the header in the archive
  1062.         $this->_writeBlock($v_binary_data_first, 148);
  1063.  
  1064.         // ----- Write the calculated checksum
  1065.         $v_checksum = sprintf("%6s ", DecOct($v_checksum));
  1066.         $v_binary_data = pack("a8", $v_checksum);
  1067.         $this->_writeBlock($v_binary_data, 8);
  1068.  
  1069.         // ----- Write the last 356 bytes of the header in the archive
  1070.         $this->_writeBlock($v_binary_data_last, 356);
  1071.  
  1072.         // ----- Write the filename as content of the block
  1073.         $i=0;
  1074.         while (($v_buffer = substr($p_filename, (($i++)*512), 512)) != '') {
  1075.             $v_binary_data = pack("a512", "$v_buffer");
  1076.             $this->_writeBlock($v_binary_data);
  1077.         }
  1078.  
  1079.         return true;
  1080.     }
  1081.     // }}}
  1082.  
  1083.     // {{{ _readHeader()
  1084.     function _readHeader($v_binary_data, &$v_header)
  1085.     {
  1086.         if (strlen($v_binary_data)==0) {
  1087.             $v_header['filename'] = '';
  1088.             return true;
  1089.         }
  1090.  
  1091.         if (strlen($v_binary_data) != 512) {
  1092.             $v_header['filename'] = '';
  1093.             $this->_error('Invalid block size : '.strlen($v_binary_data));
  1094.             return false;
  1095.         }
  1096.  
  1097.         // ----- Calculate the checksum
  1098.         $v_checksum = 0;
  1099.         // ..... First part of the header
  1100.         for ($i=0; $i<148; $i++)
  1101.             $v_checksum+=ord(substr($v_binary_data,$i,1));
  1102.         // ..... Ignore the checksum value and replace it by ' ' (space)
  1103.         for ($i=148; $i<156; $i++)
  1104.             $v_checksum += ord(' ');
  1105.         // ..... Last part of the header
  1106.         for ($i=156; $i<512; $i++)
  1107.            $v_checksum+=ord(substr($v_binary_data,$i,1));
  1108.  
  1109.         $v_data = unpack("a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/a8checksum/a1typeflag/a100link/a6magic/a2version/a32uname/a32gname/a8devmajor/a8devminor", $v_binary_data);
  1110.  
  1111.         // ----- Extract the checksum
  1112.         $v_header['checksum'] = OctDec(trim($v_data['checksum']));
  1113.         if ($v_header['checksum'] != $v_checksum) {
  1114.             $v_header['filename'] = '';
  1115.  
  1116.             // ----- Look for last block (empty block)
  1117.             if (($v_checksum == 256) && ($v_header['checksum'] == 0))
  1118.                 return true;
  1119.  
  1120.             $this->_error('Invalid checksum for file "'.$v_data['filename'].'" : '.$v_checksum.' calculated, '.$v_header['checksum'].' expected');
  1121.             return false;
  1122.         }
  1123.  
  1124.         // ----- Extract the properties
  1125.         $v_header['filename'] = trim($v_data['filename']);
  1126.         $v_header['mode'] = OctDec(trim($v_data['mode']));
  1127.         $v_header['uid'] = OctDec(trim($v_data['uid']));
  1128.         $v_header['gid'] = OctDec(trim($v_data['gid']));
  1129.         $v_header['size'] = OctDec(trim($v_data['size']));
  1130.         $v_header['mtime'] = OctDec(trim($v_data['mtime']));
  1131.         if (($v_header['typeflag'] = $v_data['typeflag']) == "5") {
  1132.           $v_header['size'] = 0;
  1133.         }
  1134.         /* ----- All these fields are removed form the header because they do not carry interesting info
  1135.         $v_header[link] = trim($v_data[link]);
  1136.         $v_header[magic] = trim($v_data[magic]);
  1137.         $v_header[version] = trim($v_data[version]);
  1138.         $v_header[uname] = trim($v_data[uname]);
  1139.         $v_header[gname] = trim($v_data[gname]);
  1140.         $v_header[devmajor] = trim($v_data[devmajor]);
  1141.         $v_header[devminor] = trim($v_data[devminor]);
  1142.         */
  1143.  
  1144.         return true;
  1145.     }
  1146.     // }}}
  1147.  
  1148.     // {{{ _readLongHeader()
  1149.     function _readLongHeader(&$v_header)
  1150.     {
  1151.       $v_filename = '';
  1152.       $n = floor($v_header['size']/512);
  1153.       for ($i=0; $i<$n; $i++) {
  1154.         $v_content = $this->_readBlock();
  1155.         $v_filename .= $v_content;
  1156.       }
  1157.       if (($v_header['size'] % 512) != 0) {
  1158.         $v_content = $this->_readBlock();
  1159.         $v_filename .= $v_content;
  1160.       }
  1161.  
  1162.       // ----- Read the next header
  1163.       $v_binary_data = $this->_readBlock();
  1164.  
  1165.       if (!$this->_readHeader($v_binary_data, $v_header))
  1166.         return false;
  1167.  
  1168.       $v_header['filename'] = $v_filename;
  1169.  
  1170.       return true;
  1171.     }
  1172.     // }}}
  1173.  
  1174.     // {{{ _extractInString()
  1175.     /**
  1176.     * This method extract from the archive one file identified by $p_filename.
  1177.     * The return value is a string with the file content, or NULL on error.
  1178.     * @param string $p_filename     The path of the file to extract in a string.
  1179.     * @return                       a string with the file content or NULL.
  1180.     * @access private
  1181.     */
  1182.     function _extractInString($p_filename)
  1183.     {
  1184.         $v_result_str = "";
  1185.  
  1186.         While (strlen($v_binary_data = $this->_readBlock()) != 0)
  1187.         {
  1188.           if (!$this->_readHeader($v_binary_data, $v_header))
  1189.             return NULL;
  1190.  
  1191.           if ($v_header['filename'] == '')
  1192.             continue;
  1193.  
  1194.           // ----- Look for long filename
  1195.           if ($v_header['typeflag'] == 'L') {
  1196.             if (!$this->_readLongHeader($v_header))
  1197.               return NULL;
  1198.           }
  1199.  
  1200.           if ($v_header['filename'] == $p_filename) {
  1201.               if ($v_header['typeflag'] == "5") {
  1202.                   $this->_error('Unable to extract in string a directory entry {'.$v_header['filename'].'}');
  1203.                   return NULL;
  1204.               } else {
  1205.                   $n = floor($v_header['size']/512);
  1206.                   for ($i=0; $i<$n; $i++) {
  1207.                       $v_result_str .= $this->_readBlock();
  1208.                   }
  1209.                   if (($v_header['size'] % 512) != 0) {
  1210.                       $v_content = $this->_readBlock();
  1211.                       $v_result_str .= substr($v_content, 0, ($v_header['size'] % 512));
  1212.                   }
  1213.                   return $v_result_str;
  1214.               }
  1215.           } else {
  1216.               $this->_jumpBlock(ceil(($v_header['size']/512)));
  1217.           }
  1218.         }
  1219.  
  1220.         return NULL;
  1221.     }
  1222.     // }}}
  1223.  
  1224.     // {{{ _extractList()
  1225.     function _extractList($p_path, &$p_list_detail, $p_mode, $p_file_list, $p_remove_path)
  1226.     {
  1227.     $v_result=true;
  1228.     $v_nb = 0;
  1229.     $v_extract_all = true;
  1230.     $v_listing = false;
  1231.  
  1232.     $p_path = $this->_translateWinPath($p_path, false);
  1233.     if ($p_path == '' || (substr($p_path, 0, 1) != '/' && substr($p_path, 0, 3) != "../" && !strpos($p_path, ':'))) {
  1234.       $p_path = "./".$p_path;
  1235.     }
  1236.     $p_remove_path = $this->_translateWinPath($p_remove_path);
  1237.  
  1238.     // ----- Look for path to remove format (should end by /)
  1239.     if (($p_remove_path != '') && (substr($p_remove_path, -1) != '/'))
  1240.       $p_remove_path .= '/';
  1241.     $p_remove_path_size = strlen($p_remove_path);
  1242.  
  1243.     switch ($p_mode) {
  1244.       case "complete" :
  1245.         $v_extract_all = TRUE;
  1246.         $v_listing = FALSE;
  1247.       break;
  1248.       case "partial" :
  1249.           $v_extract_all = FALSE;
  1250.           $v_listing = FALSE;
  1251.       break;
  1252.       case "list" :
  1253.           $v_extract_all = FALSE;
  1254.           $v_listing = TRUE;
  1255.       break;
  1256.       default :
  1257.         $this->_error('Invalid extract mode ('.$p_mode.')');
  1258.         return false;
  1259.     }
  1260.  
  1261.     clearstatcache();
  1262.  
  1263.     While (strlen($v_binary_data = $this->_readBlock()) != 0)
  1264.     {
  1265.       $v_extract_file = FALSE;
  1266.       $v_extraction_stopped = 0;
  1267.  
  1268.       if (!$this->_readHeader($v_binary_data, $v_header))
  1269.         return false;
  1270.  
  1271.       if ($v_header['filename'] == '')
  1272.         continue;
  1273.  
  1274.       // ----- Look for long filename
  1275.       if ($v_header['typeflag'] == 'L') {
  1276.         if (!$this->_readLongHeader($v_header))
  1277.           return false;
  1278.       }
  1279.  
  1280.       if ((!$v_extract_all) && (is_array($p_file_list))) {
  1281.         // ----- By default no unzip if the file is not found
  1282.         $v_extract_file = false;
  1283.  
  1284.         for ($i=0; $i<sizeof($p_file_list); $i++) {
  1285.           // ----- Look if it is a directory
  1286.           if (substr($p_file_list[$i], -1) == '/') {
  1287.             // ----- Look if the directory is in the filename path
  1288.             if ((strlen($v_header['filename']) > strlen($p_file_list[$i])) && (substr($v_header['filename'], 0, strlen($p_file_list[$i])) == $p_file_list[$i])) {
  1289.               $v_extract_file = TRUE;
  1290.               break;
  1291.             }
  1292.           }
  1293.  
  1294.           // ----- It is a file, so compare the file names
  1295.           elseif ($p_file_list[$i] == $v_header['filename']) {
  1296.             $v_extract_file = TRUE;
  1297.             break;
  1298.           }
  1299.         }
  1300.       } else {
  1301.         $v_extract_file = TRUE;
  1302.       }
  1303.  
  1304.       // ----- Look if this file need to be extracted
  1305.       if (($v_extract_file) && (!$v_listing))
  1306.       {
  1307.         if (($p_remove_path != '')
  1308.             && (substr($v_header['filename'], 0, $p_remove_path_size) == $p_remove_path))
  1309.           $v_header['filename'] = substr($v_header['filename'], $p_remove_path_size);
  1310.         if (($p_path != './') && ($p_path != '/')) {
  1311.           while (substr($p_path, -1) == '/')
  1312.             $p_path = substr($p_path, 0, strlen($p_path)-1);
  1313.  
  1314.           if (substr($v_header['filename'], 0, 1) == '/')
  1315.               $v_header['filename'] = $p_path.$v_header['filename'];
  1316.           else
  1317.             $v_header['filename'] = $p_path.'/'.$v_header['filename'];
  1318.         }
  1319.         if (file_exists($v_header['filename'])) {
  1320.           if ((@is_dir($v_header['filename'])) && ($v_header['typeflag'] == '')) {
  1321.             $this->_error('File '.$v_header['filename'].' already exists as a directory');
  1322.             return false;
  1323.           }
  1324.           if ((is_file($v_header['filename'])) && ($v_header['typeflag'] == "5")) {
  1325.             $this->_error('Directory '.$v_header['filename'].' already exists as a file');
  1326.             return false;
  1327.           }
  1328.           if (!is_writeable($v_header['filename'])) {
  1329.             $this->_error('File '.$v_header['filename'].' already exists and is write protected');
  1330.             return false;
  1331.           }
  1332.           if (filemtime($v_header['filename']) > $v_header['mtime']) {
  1333.             // To be completed : An error or silent no replace ?
  1334.           }
  1335.         }
  1336.  
  1337.         // ----- Check the directory availability and create it if necessary
  1338.         elseif (($v_result = $this->_dirCheck(($v_header['typeflag'] == "5"?$v_header['filename']:dirname($v_header['filename'])))) != 1) {
  1339.             $this->_error('Unable to create path for '.$v_header['filename']);
  1340.             return false;
  1341.         }
  1342.  
  1343.         if ($v_extract_file) {
  1344.           if ($v_header['typeflag'] == "5") {
  1345.             if (!@file_exists($v_header['filename'])) {
  1346.                 if (!@mkdir($v_header['filename'], 0777)) {
  1347.                     $this->_error('Unable to create directory {'.$v_header['filename'].'}');
  1348.                     return false;
  1349.                 }
  1350.             }
  1351.           } else {
  1352.               if (($v_dest_file = @fopen($v_header['filename'], "wb")) == 0) {
  1353.                   $this->_error('Error while opening {'.$v_header['filename'].'} in write binary mode');
  1354.                   return false;
  1355.               } else {
  1356.                   $n = floor($v_header['size']/512);
  1357.                   for ($i=0; $i<$n; $i++) {
  1358.                       $v_content = $this->_readBlock();
  1359.                       fwrite($v_dest_file, $v_content, 512);
  1360.                   }
  1361.             if (($v_header['size'] % 512) != 0) {
  1362.               $v_content = $this->_readBlock();
  1363.               fwrite($v_dest_file, $v_content, ($v_header['size'] % 512));
  1364.             }
  1365.  
  1366.             @fclose($v_dest_file);
  1367.  
  1368.             // ----- Change the file mode, mtime
  1369.             @touch($v_header['filename'], $v_header['mtime']);
  1370.             // To be completed
  1371.             //chmod($v_header[filename], DecOct($v_header[mode]));
  1372.           }
  1373.  
  1374.           // ----- Check the file size
  1375.           clearstatcache();
  1376.           if (filesize($v_header['filename']) != $v_header['size']) {
  1377.               $this->_error('Extracted file '.$v_header['filename'].' does not have the correct file size \''.filesize($v_filename).'\' ('.$v_header['size'].' expected). Archive may be corrupted.');
  1378.               return false;
  1379.           }
  1380.           }
  1381.         } else {
  1382.           $this->_jumpBlock(ceil(($v_header['size']/512)));
  1383.         }
  1384.       } else {
  1385.           $this->_jumpBlock(ceil(($v_header['size']/512)));
  1386.       }
  1387.  
  1388.       /* TBC : Seems to be unused ...
  1389.       if ($this->_compress)
  1390.         $v_end_of_file = @gzeof($this->_file);
  1391.       else
  1392.         $v_end_of_file = @feof($this->_file);
  1393.         */
  1394.  
  1395.       if ($v_listing || $v_extract_file || $v_extraction_stopped) {
  1396.         // ----- Log extracted files
  1397.         if (($v_file_dir = dirname($v_header['filename'])) == $v_header['filename'])
  1398.           $v_file_dir = '';
  1399.         if ((substr($v_header['filename'], 0, 1) == '/') && ($v_file_dir == ''))
  1400.           $v_file_dir = '/';
  1401.  
  1402.         $p_list_detail[$v_nb++] = $v_header;
  1403.       }
  1404.     }
  1405.  
  1406.         return true;
  1407.     }
  1408.     // }}}
  1409.  
  1410.     // {{{ _openAppend()
  1411.     function _openAppend()
  1412.     {
  1413.         if (filesize($this->_tarname) == 0)
  1414.           return $this->_openWrite();
  1415.           
  1416.         if ($this->_compress) {
  1417.             $this->_close();
  1418.  
  1419.             if (!@rename($this->_tarname, $this->_tarname.".tmp")) {
  1420.                 $this->_error('Error while renaming \''.$this->_tarname.'\' to temporary file \''.$this->_tarname.'.tmp\'');
  1421.                 return false;
  1422.             }
  1423.  
  1424.             if ($this->_compress_type == 'gz')
  1425.                 $v_temp_tar = @gzopen($this->_tarname.".tmp", "rb");
  1426.             elseif ($this->_compress_type == 'bz2')
  1427.                 $v_temp_tar = @bzopen($this->_tarname.".tmp", "rb");
  1428.                 
  1429.             if ($v_temp_tar == 0) {
  1430.                 $this->_error('Unable to open file \''.$this->_tarname.'.tmp\' in binary read mode');
  1431.                 @rename($this->_tarname.".tmp", $this->_tarname);
  1432.                 return false;
  1433.             }
  1434.  
  1435.             if (!$this->_openWrite()) {
  1436.                 @rename($this->_tarname.".tmp", $this->_tarname);
  1437.                 return false;
  1438.             }
  1439.  
  1440.             if ($this->_compress_type == 'gz') {
  1441.                 $v_buffer = @gzread($v_temp_tar, 512);
  1442.  
  1443.                 // ----- Read the following blocks but not the last one
  1444.                 if (!@gzeof($v_temp_tar)) {
  1445.                     do{
  1446.                         $v_binary_data = pack("a512", $v_buffer);
  1447.                         $this->_writeBlock($v_binary_data);
  1448.                         $v_buffer = @gzread($v_temp_tar, 512);
  1449.  
  1450.                     } while (!@gzeof($v_temp_tar));
  1451.                 }
  1452.  
  1453.                 @gzclose($v_temp_tar);
  1454.             }
  1455.             elseif ($this->_compress_type == 'bz2') {
  1456.                 $v_buffered_lines   = array();
  1457.                 $v_buffered_lines[] = @bzread($v_temp_tar, 512);
  1458.  
  1459.                 // ----- Read the following blocks but not the last one
  1460.                 while (strlen($v_buffered_lines[] = @bzread($v_temp_tar, 512)) > 0) {
  1461.                     $v_binary_data = pack("a512", array_shift($v_buffered_lines));
  1462.                     $this->_writeBlock($v_binary_data);
  1463.                 }
  1464.  
  1465.                 @bzclose($v_temp_tar);
  1466.             }
  1467.  
  1468.             if (!@unlink($this->_tarname.".tmp")) {
  1469.                 $this->_error('Error while deleting temporary file \''.$this->_tarname.'.tmp\'');
  1470.             }
  1471.  
  1472.         } else {
  1473.             // ----- For not compressed tar, just add files before the last 512 bytes block
  1474.             if (!$this->_openReadWrite())
  1475.                return false;
  1476.  
  1477.             clearstatcache();
  1478.             $v_size = filesize($this->_tarname);
  1479.             fseek($this->_file, $v_size-512);
  1480.         }
  1481.  
  1482.         return true;
  1483.     }
  1484.     // }}}
  1485.  
  1486.     // {{{ _append()
  1487.     function _append($p_filelist, $p_add_dir='', $p_remove_dir='')
  1488.     {
  1489.         if (!$this->_openAppend())
  1490.             return false;
  1491.             
  1492.         if ($this->_addList($p_filelist, $p_add_dir, $p_remove_dir))
  1493.            $this->_writeFooter();
  1494.  
  1495.         $this->_close();
  1496.  
  1497.         return true;
  1498.     }
  1499.     // }}}
  1500.  
  1501.     // {{{ _dirCheck()
  1502.  
  1503.     /**
  1504.      * Check if a directory exists and create it (including parent
  1505.      * dirs) if not.
  1506.      *
  1507.      * @param string $p_dir directory to check
  1508.      *
  1509.      * @return bool TRUE if the directory exists or was created
  1510.      */
  1511.     function _dirCheck($p_dir)
  1512.     {
  1513.         if ((@is_dir($p_dir)) || ($p_dir == ''))
  1514.             return true;
  1515.  
  1516.         $p_parent_dir = dirname($p_dir);
  1517.  
  1518.         if (($p_parent_dir != $p_dir) &&
  1519.             ($p_parent_dir != '') &&
  1520.             (!$this->_dirCheck($p_parent_dir)))
  1521.              return false;
  1522.  
  1523.         if (!@mkdir($p_dir, 0777)) {
  1524.             $this->_error("Unable to create directory '$p_dir'");
  1525.             return false;
  1526.         }
  1527.  
  1528.         return true;
  1529.     }
  1530.  
  1531.     // }}}
  1532.  
  1533.     // {{{ _pathReduction()
  1534.  
  1535.     /**
  1536.      * Compress path by changing for example "/dir/foo/../bar" to "/dir/bar", and
  1537.      * remove double slashes.
  1538.      *
  1539.      * @param string $p_dir path to reduce
  1540.      *
  1541.      * @return string reduced path
  1542.      *
  1543.      * @access private
  1544.      *
  1545.      */
  1546.     function _pathReduction($p_dir)
  1547.     {
  1548.         $v_result = '';
  1549.  
  1550.         // ----- Look for not empty path
  1551.         if ($p_dir != '') {
  1552.             // ----- Explode path by directory names
  1553.             $v_list = explode('/', $p_dir);
  1554.  
  1555.             // ----- Study directories from last to first
  1556.             for ($i=sizeof($v_list)-1; $i>=0; $i--) {
  1557.                 // ----- Look for current path
  1558.                 if ($v_list[$i] == ".") {
  1559.                     // ----- Ignore this directory
  1560.                     // Should be the first $i=0, but no check is done
  1561.                 }
  1562.                 else if ($v_list[$i] == "..") {
  1563.                     // ----- Ignore it and ignore the $i-1
  1564.                     $i--;
  1565.                 }
  1566.                 else if (($v_list[$i] == '') && ($i!=(sizeof($v_list)-1)) && ($i!=0)) {
  1567.                     // ----- Ignore only the double '//' in path,
  1568.                     // but not the first and last /
  1569.                 } else {
  1570.                     $v_result = $v_list[$i].($i!=(sizeof($v_list)-1)?'/'.$v_result:'');
  1571.                 }
  1572.             }
  1573.         }
  1574.         $v_result = strtr($v_result, '\\', '/');
  1575.         return $v_result;
  1576.     }
  1577.  
  1578.     // }}}
  1579.  
  1580.     // {{{ _translateWinPath()
  1581.     function _translateWinPath($p_path, $p_remove_disk_letter=true)
  1582.     {
  1583.       if (OS_WINDOWS) {
  1584.           // ----- Look for potential disk letter
  1585.           if (($p_remove_disk_letter) && (($v_position = strpos($p_path, ':')) != false)) {
  1586.               $p_path = substr($p_path, $v_position+1);
  1587.           }
  1588.           // ----- Change potential windows directory separator
  1589.           if ((strpos($p_path, '\\') > 0) || (substr($p_path, 0,1) == '\\')) {
  1590.               $p_path = strtr($p_path, '\\', '/');
  1591.           }
  1592.       }
  1593.       return $p_path;
  1594.     }
  1595.     // }}}
  1596.  
  1597. }
  1598. ?>
  1599.